home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 14 - Glypha III / Glypha III source / GlyphaIII Code ƒ / Utilities.c < prev   
Text File  |  1995-03-24  |  18KB  |  506 lines

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Utilities.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // These functions are sort of universal utility functions.  They aren't specific…
  9. // to Glypha per se.  I use these (and others) in many, many games.  Many of them…
  10. // as well are useful for any app you might write for the Mac.
  11.  
  12. #include "Externs.h"
  13.  
  14.  
  15. #define kActive                        0
  16. #define kInactive                    255
  17.  
  18.  
  19. GDHandle    thisGDevice;
  20. long        tickNext;
  21.  
  22.  
  23. //==============================================================  Functions
  24. //--------------------------------------------------------------  RandomInt
  25.  
  26. // Takes a short (range) and returns a random number from zero to range - 1.
  27.  
  28. short RandomInt (short range)
  29. {
  30.     register long    rawResult;
  31.     
  32.     rawResult = Random();
  33.     if (rawResult < 0L)
  34.         rawResult *= -1L;
  35.     rawResult = (rawResult * (long)range) / 32768L;
  36.     
  37.     return ((short)rawResult);
  38. }
  39.  
  40. //--------------------------------------------------------------  RedAlert
  41.  
  42. // Generic error function.  This is called when there is no hope of recovering
  43. // from the error.  A simple alert is brought up and the text passed in (theStr)
  44. // is displayed.  When the user clicks the Okay button, we quit to the Finder.
  45.  
  46. void RedAlert (StringPtr theStr)
  47. {
  48.     #define        kRedAlertID        128
  49.     short        whoCares;
  50.     
  51.     ParamText(theStr, "\p", "\p", "\p");        // Replace ^0 in alert with error mssg.
  52.     whoCares = Alert(kRedAlertID, 0L);            // Bring up alert.
  53.     ExitToShell();                                // Quit to Finder.
  54. }
  55.  
  56. //--------------------------------------------------------------  FindOurDevice
  57.  
  58. // Get a handle to the MainDevice (monitor with the Menubar).
  59.  
  60. void FindOurDevice (void)
  61. {
  62.     thisGDevice = GetMainDevice();
  63.     if (thisGDevice == 0L)                        // If a nil handle is returned...
  64.         RedAlert("\pCouldn't Find Our Device");    // call our universal error alert.
  65. }
  66.  
  67. //--------------------------------------------------------------  LoadGraphic
  68.  
  69. // Handy function that loads a PICT graphic, get's its bounds and draws it.
  70. // The port drawn to is assumed the current port.  No scaling is done.
  71.  
  72. void LoadGraphic (short resID)
  73. {
  74.     Rect        bounds;
  75.     PicHandle    thePicture;
  76.     
  77.     thePicture = GetPicture(resID);                // Load graphic from resource fork.
  78.     if (thePicture == 0L)                        // Check to see if nil (did it load?)
  79.         RedAlert("\pA Graphic Couldn't Be Loaded");
  80.     
  81.     HLock((Handle)thePicture);                    // If we made it this far, lock handle.
  82.     bounds = (*thePicture)->picFrame;            // Get a copy of the picture's bounds.
  83.     HUnlock((Handle)thePicture);                // We can unlock the picture now.
  84.     OffsetRect(&bounds, -bounds.left, -bounds.top);    // Offset bounds rect to (0, 0).
  85.     DrawPicture(thePicture, &bounds);            // Draw picture to current port.
  86.     
  87.     ReleaseResource((Handle)thePicture);        // Dispose of picture from heap.
  88. }
  89.  
  90. //--------------------------------------------------------------  CreateOffScreenPixMap
  91.  
  92. // Handles the creation of an offscreen pixmap.  Depth is assumed to be that of the…
  93. // current gDevice.  If the allocation fails (low memory, etc.) we quit to Finder.
  94.  
  95. void CreateOffScreenPixMap (Rect *theRect, CGrafPtr *offScreen)
  96. {
  97.     CTabHandle    thisColorTable;
  98.     GDHandle    oldDevice;
  99.     CGrafPtr    newCGrafPtr;
  100.     Ptr            theseBits;
  101.     long        sizeOfOff, offRowBytes;
  102.     OSErr        theErr;
  103.     short        thisDepth;
  104.     
  105.     oldDevice = GetGDevice();
  106.     SetGDevice(thisGDevice);
  107.     newCGrafPtr = 0L;
  108.     newCGrafPtr = (CGrafPtr)NewPtrClear(sizeof(CGrafPort));
  109.     if (newCGrafPtr != 0L)
  110.     {
  111.         OpenCPort(newCGrafPtr);
  112.         thisDepth = (**(*newCGrafPtr).portPixMap).pixelSize;
  113.         offRowBytes = ((((long)thisDepth * 
  114.                 (long)(theRect->right - theRect->left)) + 15L) >> 4L) << 1L;
  115.         sizeOfOff = (long)(theRect->bottom - theRect->top) * offRowBytes;
  116.         OffsetRect(theRect, -theRect->left, -theRect->top);
  117.         theseBits = NewPtr(sizeOfOff);
  118.         if (theseBits != 0L)
  119.         {
  120.             (**(*newCGrafPtr).portPixMap).baseAddr = theseBits;
  121.             (**(*newCGrafPtr).portPixMap).rowBytes = (short)offRowBytes + 0x8000;
  122.             (**(*newCGrafPtr).portPixMap).bounds = *theRect;
  123.             thisColorTable = (**(**thisGDevice).gdPMap).pmTable;
  124.             theErr = HandToHand((Handle *)&thisColorTable);
  125.             (**(*newCGrafPtr).portPixMap).pmTable = thisColorTable;
  126.             ClipRect(theRect);
  127.             RectRgn(newCGrafPtr->visRgn, theRect);
  128.             ForeColor(blackColor);
  129.             BackColor(whiteColor);
  130.             EraseRect(theRect);
  131.         }
  132.         else
  133.         {
  134.             CloseCPort(newCGrafPtr);        
  135.             DisposePtr((Ptr)newCGrafPtr);
  136.             newCGrafPtr = 0L;
  137.             RedAlert("\pCouldn't Allocate Enough Memory");
  138.         }
  139.     }
  140.     else
  141.         RedAlert("\pCouldn't Allocate Enough Memory");
  142.     
  143.     *offScreen = newCGrafPtr;
  144.     SetGDevice(oldDevice);
  145. }
  146.  
  147. //--------------------------------------------------------------  CreateOffScreenBitMap
  148.  
  149. // Creates an offscreen bitmap.  Depth is of course 1 (b & w).  If this function…
  150. // fails to create the bitmap, we post an alert and quit to the Finder.
  151.  
  152. void CreateOffScreenBitMap (Rect *theRect, GrafPtr *offScreen)
  153. {
  154.     GrafPtr        theBWPort;
  155.     BitMap        theBitMap;    
  156.     long        theRowBytes;
  157.     
  158.     theBWPort = (GrafPtr)(NewPtr(sizeof(GrafPort)));
  159.     OpenPort(theBWPort);
  160.     theRowBytes = (long)((theRect->right - theRect->left + 15L) / 16L) * 2L;
  161.     theBitMap.rowBytes = (short)theRowBytes;
  162.     theBitMap.baseAddr = NewPtr((long)theBitMap.rowBytes * 
  163.         (theRect->bottom - theRect->top));
  164.     if (theBitMap.baseAddr == 0L)
  165.         RedAlert("\pCouldn't Create Bitmaps");
  166.     theBitMap.bounds = *theRect;
  167.     if (MemError() != noErr)
  168.         RedAlert("\pCouldn't Create Bitmaps");
  169.     SetPortBits(&theBitMap);
  170.     ClipRect(theRect);
  171.     RectRgn(theBWPort->visRgn, theRect);
  172.     EraseRect(theRect);
  173.     *offScreen = theBWPort;
  174. }
  175.  
  176. //--------------------------------------------------------------  ZeroRectCorner
  177.  
  178. // Offset rect to (0, 0).  This means the upper left corner of the rect is 
  179. // moved to the origin - to (0, 0) - to the upperleft corner of the port.
  180.  
  181. void ZeroRectCorner (Rect *theRect)
  182. {
  183.     theRect->right -= theRect->left;    // Move right edge by amount of left.
  184.     theRect->bottom -= theRect->top;    // Move bottom edge by amount of top.
  185.     theRect->left = 0;                    // Can now set left to zero.
  186.     theRect->top = 0;                    // Can set top edge to zero as well.
  187. }
  188.  
  189. //--------------------------------------------------------------  FlashShort
  190.  
  191. // This is a simple debugging function that will display the short passed to it…
  192. // in the upper left corner of the screen.  It's a handy way to watch the value…
  193. // of a variable while the program is running.
  194.  
  195. void FlashShort (short theValue)
  196. {
  197.     GrafPtr            wasPort, tempPort;
  198.     Str255            tempStr;
  199.     Rect            tempRect;
  200.     
  201.     GetPort(&wasPort);                        // Remember old grafPort.
  202.     
  203.     tempPort = (GrafPtr)NewPtrClear(sizeof(GrafPort));
  204.     OpenPort(tempPort);                        // Create a new empty port.
  205.     
  206.     NumToString((long)theValue, tempStr);    // Convert value passed in to a string.
  207.     MoveTo(20, 40);                            // Move the pen to the upperleft corner.
  208.     SetRect(&tempRect, 18, 20, 122, 42);    // Create a rect up there as well.
  209.     EraseRect(&tempRect);                    // Erase the rect (to make a white hole).
  210.     DrawString(tempStr);                    // And draw our text into that hole.
  211.     
  212.     ClosePort(tempPort);                    // Get rid of out temp port.
  213.     SetPort((GrafPtr)wasPort);                // And set port back to the old one.
  214. }
  215.  
  216. //--------------------------------------------------------------  LogNextTick
  217.  
  218. // Simple function to set a global (tickNext) to the current TickCount() plus…
  219. // some offset.  We'll then wait for TickCount() to exceed that global.  We use…
  220. // this function and the function below to regulate animation speeds (remember…
  221. // your game may be run on a slow Mac or a fast one - we need a way to keep the…
  222. // motion consistent.  I love when the comments are longer than the function.
  223. // (Not really.)
  224.  
  225. void LogNextTick (long howMany)
  226. {
  227.     tickNext = TickCount() + howMany;        // Get machine's TickCount() and add to it.
  228. }
  229.  
  230. //--------------------------------------------------------------  WaitForNextTick
  231.  
  232. // This is the companion function to the above function (LogNextTick()).
  233. // We do nothing but loop until TickCount() catches up with (or passes) our…
  234. // global variable tickNext.
  235.  
  236. void WaitForNextTick (void)
  237. {
  238.     do
  239.     {
  240.     }
  241.     while (TickCount() < tickNext);            // Loop until TickCount() catches up.
  242. }
  243.  
  244. //--------------------------------------------------------------  TrapExists  
  245.  
  246. // A nice "test function" that test for the existence of some ToolBox trap.
  247. // Returns TRUE if the function exists, FALSE if it doesn't.
  248.  
  249. Boolean TrapExists (short trapNumber)
  250. {
  251.     #define        kUnimpTrap        0x9F
  252.     
  253.                 // Test trap number against unimplemented trap number.
  254.     return ((NGetTrapAddress(trapNumber, ToolTrap) !=
  255.             NGetTrapAddress(kUnimpTrap, ToolTrap)));
  256. }
  257.  
  258. //--------------------------------------------------------------  DoWeHaveGestalt  
  259.  
  260. // This function specifically tests for the availablity of the Gestalt() function.
  261. // It returns TRUE if Gestalt() exists, FALSE if it doesn't.
  262.  
  263. Boolean DoWeHaveGestalt (void)
  264. {
  265.     #define        kGestaltTrap    0xAD
  266.     
  267.                 // Call above function (TrapExists()) with the Gestalt() trap number.
  268.     return (TrapExists(kGestaltTrap));
  269. }
  270.  
  271. //--------------------------------------------------------------  CenterAlert
  272.  
  273. // Handy function to center any alert within the main monitor.
  274.  
  275. void CenterAlert (short alertID)
  276. {
  277.     AlertTHndl    alertHandle;
  278.     Rect        theScreen, alertRect;
  279.     short        horiOff, vertOff;
  280.     Byte        wasState;
  281.     
  282.     theScreen = qd.screenBits.bounds;        // Get main monitor's bounds.
  283.     theScreen.top += LMGetMBarHeight();        // Account for menubar height.
  284.                                             // Get handle to alert resource.
  285.     alertHandle = (AlertTHndl)GetResource('ALRT', alertID);
  286.     if (alertHandle != 0L)                    // Make sure we got it!
  287.     {                                        // Remember its "state" (locked, etc.)
  288.         wasState = HGetState((Handle)alertHandle);
  289.         HLock((Handle)alertHandle);            // We'll lock it.
  290.                                             // Get a copy of it's bounds and zero.
  291.         alertRect = (**alertHandle).boundsRect;
  292.         OffsetRect(&alertRect, -alertRect.left, -alertRect.top);
  293.                                             // Calculate offsets for centering bounds.
  294.         horiOff = ((theScreen.right - theScreen.left) - alertRect.right) / 2;    
  295.         vertOff = ((theScreen.bottom - theScreen.top) - alertRect.bottom) / 3;
  296.                                             // And offset the bounds copy.
  297.         OffsetRect(&alertRect, horiOff, vertOff + LMGetMBarHeight());
  298.                                             // Set alerts bounds to our centered rect.
  299.         (**alertHandle).boundsRect = alertRect;
  300.         HSetState((Handle)alertHandle, wasState);
  301.     }
  302. }
  303.  
  304. //--------------------------------------------------------------  RectWide
  305.  
  306. // Handy function for returning the absolute width of a rectangle.
  307.  
  308. short RectWide (Rect *theRect)
  309. {
  310.     return (theRect->right - theRect->left);
  311. }
  312.  
  313. //--------------------------------------------------------------  RectTall
  314.  
  315. // Handy function for returning the absolute height of a rectangle.
  316.  
  317. short RectTall (Rect *theRect)
  318. {
  319.     return (theRect->bottom - theRect->top);
  320. }
  321.  
  322. //--------------------------------------------------------------  CenterRectInRect
  323.  
  324. // Nice utility function that takes two rectangles and centers the first…
  325. // rectangle within the second.
  326.  
  327. void CenterRectInRect (Rect *rectA, Rect *rectB)
  328. {
  329.     short    widthA, tallA;
  330.     
  331.     widthA = RectWide(rectA);                // Get width of 1st rect.
  332.     tallA = RectTall(rectA);                // Get height of 1st rect.
  333.                                             // Do the math (center horizontally).
  334.     rectA->left = rectB->left + (RectWide(rectB) - widthA) / 2;
  335.     rectA->right = rectA->left + widthA;
  336.                                             // Do the math (center vertically).
  337.     rectA->top = rectB->top + (RectTall(rectB) - tallA) / 2;
  338.     rectA->bottom = rectA->top + tallA;
  339. }
  340.  
  341. //--------------------------------------------------------------  PasStringCopy
  342.  
  343. // This is a nice function that helps to free you from dealing with C strings.
  344. // It takes one Pascal-style string and copies it to a second.
  345.  
  346. void PasStringCopy (StringPtr p1, StringPtr p2)
  347. {
  348.     register short        stringLength;
  349.     
  350.     stringLength = *p2++ = *p1++;    // Get 1st string's length.
  351.     while (--stringLength >= 0)        // Loop through each character in 1st string.
  352.         *p2++ = *p1++;                // And copy to 2nd string.
  353. }
  354.  
  355. //--------------------------------------------------------------  CenterDialog
  356.  
  357. // Like CenterAlert(), this function centers a Dialog on the main monitor.
  358.  
  359. void CenterDialog (short dialogID)
  360. {
  361.     DialogTHndl    dlogHandle;
  362.     Rect        theScreen, dlogBounds;
  363.     short        hPos, vPos;
  364.     Byte        wasState;
  365.     
  366.     theScreen = qd.screenBits.bounds;            // Get main monitor's bounds.
  367.     theScreen.top += LMGetMBarHeight();            // Add menuBar's height.
  368.                                                 // Load up dialog from resource.
  369.     dlogHandle = (DialogTHndl)GetResource('DLOG', dialogID);
  370.     if (dlogHandle != 0L)                        // If it loaded....!
  371.     {                                            // Remember handle state.
  372.         wasState = HGetState((Handle)dlogHandle);
  373.         HLock((Handle)dlogHandle);                // We're going to lock it.
  374.                                                 // Get a copy of the dialog's bounds.
  375.         dlogBounds = (**dlogHandle).boundsRect;
  376.         OffsetRect(&dlogBounds, -dlogBounds.left, -dlogBounds.top);
  377.                                                 // Calculate how much to offset.
  378.         hPos = ((theScreen.right - theScreen.left) - dlogBounds.right) / 2;
  379.         vPos = ((theScreen.bottom - theScreen.top) - dlogBounds.bottom) / 3;
  380.                                                 // Offset ourt copy of the bounds.
  381.         OffsetRect(&dlogBounds, hPos, vPos + LMGetMBarHeight());
  382.                                                 // Set dlg's bounds to centered rect.
  383.         (**dlogHandle).boundsRect = dlogBounds;
  384.         HSetState((Handle)dlogHandle, wasState);// Restore handle's state.
  385.     }
  386. }
  387.  
  388. //--------------------------------------------------------------  DrawDefaultButton
  389.  
  390. // A nice dialog function.  This draws the bold default outline around…
  391. // item #1 in the dialog passed in.
  392.  
  393. void DrawDefaultButton (DialogPtr theDialog)
  394. {
  395.     Rect        itemRect;
  396.     Handle        itemHandle;
  397.     short        itemType;
  398.                                         // Get at the item's bounds.
  399.     GetDItem(theDialog, 1, &itemType, &itemHandle, &itemRect);
  400.     InsetRect(&itemRect, -4, -4);        // Inset (outset?) bounds by -4 pixels.
  401.     PenSize(3, 3);                        // Set the pen 3 pixels thick.
  402.     FrameRoundRect(&itemRect, 16, 16);    // Draw the button outline.
  403.     PenNormal();                        // And restore pen to 1 pixel thick.
  404. }
  405.  
  406. //--------------------------------------------------------------  PasStringCopyNum
  407.  
  408. // Another function to keep you from using C strings.  This one copies only a…
  409. // certain number of characters from one Pascal-style string to a second.
  410.  
  411. void PasStringCopyNum (StringPtr p1, StringPtr p2, short charsToCopy)
  412. {
  413.     short        i;
  414.     
  415.     if (charsToCopy > *p1)        // If trying to copy more chars than there are…
  416.         charsToCopy = *p1;        // Reduce the number of chars to copy to this size
  417.     
  418.     *p2 = charsToCopy;            // Set 2nd string's length to charsToCopy.
  419.     
  420.     *p2++;                        // Point to first character in 2nd string.
  421.     *p1++;                        // Point to first character in 1st string.
  422.     
  423.     for (i = 0; i < charsToCopy; i++)
  424.         *p2++ = *p1++;            // Copy the specified number of chars over.
  425. }
  426.  
  427. //--------------------------------------------------------------  GetDialogString
  428.  
  429. // Handy dialog function that returns a dialog item string.  This will be…
  430. // especially handy for getting the high score name the player enters.
  431.  
  432. void GetDialogString (DialogPtr theDialog, short item, StringPtr theString)
  433. {
  434.     Rect        itemRect;
  435.     Handle        itemHandle;
  436.     short        itemType;
  437.                                         // Get handle to dialog item.
  438.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  439.     GetIText(itemHandle, theString);    // Extract text from item handle.
  440. }
  441.  
  442. //--------------------------------------------------------------  SetDialogString
  443.  
  444. // Like the above function, but this one sets a dialog items string to whatever…
  445. // you pass in.  We'll use this to set a default high score name.
  446.  
  447. void SetDialogString (DialogPtr theDialog, short item, StringPtr theString)
  448. {
  449.     Rect        itemRect;
  450.     Handle        itemHandle;
  451.     short        itemType;
  452.                                         // Get handle to dialog item.
  453.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  454.     SetIText(itemHandle, theString);    // Set the items text to theString.
  455. }
  456.  
  457. //--------------------------------------------------------------  SetDialogNumToStr
  458.  
  459. // This one is like SetDialogString() above, but it takes a number (long)…
  460. // instead of a string (the function will convert the long to a string for us).
  461.  
  462. void SetDialogNumToStr (DialogPtr theDialog, short item, long theNumber)
  463. {
  464.     Str255        theString;
  465.     Rect        itemRect;
  466.     Handle        itemHandle;
  467.     short        itemType;
  468.     
  469.     NumToString(theNumber, theString);    // Convert long to a string.
  470.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  471.     SetIText(itemHandle, theString);    // Set the item's text to this number/string.
  472. }
  473.  
  474. //--------------------------------------------------------------  GetDialogNumFromStr
  475.  
  476. // This one is like GetDialogString() above, but returns a long (number)…
  477. // instead of a string (it does this by converting the string to a long).
  478.  
  479. void GetDialogNumFromStr (DialogPtr theDialog, short item, long *theNumber)
  480. {
  481.     Str255        theString;
  482.     Rect        itemRect;
  483.     Handle        itemHandle;
  484.     short        itemType;
  485.                                         // Get a handle to the dialog item.
  486.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  487.     GetIText(itemHandle, theString);    // Get the item's text.
  488.     StringToNum(theString, theNumber);    // Convert the text to a long.
  489. }
  490.  
  491. //--------------------------------------------------------------  DisableControl
  492.  
  493. // Another dialog utility for "graying out" buttons or other controls in a dialog.
  494.  
  495. void DisableControl (DialogPtr theDialog, short whichItem)
  496. {
  497.     Rect        iRect;
  498.     Handle        iHandle;
  499.     short        iType;
  500.                                         // Get a handle to the dialog item.
  501.     GetDItem(theDialog, whichItem, &iType, &iHandle, &iRect);
  502.                                         // Set it's "hilite state" to "grayed out".
  503.     HiliteControl((ControlHandle)iHandle, kInactive);
  504. }
  505.  
  506.